import concurrent.futures
import requests
import urllib3
from loguru import logger
from pathlib import Path
import base64
import json
import time
import yaml
from urllib.parse import urlparse
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
from datetime import datetime
from clash_rules import get_clash_rules

# 初始化设置
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
logger = logger.bind(name="ProxyManager")

# 全局常量
USERNAME = 'admin'
PASSWORD = 'admin'
HEADERS = {
    'Accept': 'application/json, text/plain, */*',
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/102.0.5005.63 Safari/537.36',
    'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
    'X-Requested-With': 'XMLHttpRequest'
}

# Clash配置模板
CLASH_CONFIG_TEMPLATE = {
    "mixed-port": 7890,
    "allow-lan": False,
    "mode": "rule",
    "log-level": "info",
    "ipv6": False,
    "external-controller": "0.0.0.0:9090",
    "dns": {
        "enable": True,
        "listen": "0.0.0.0:53",
        "ipv6": False,
        "default-nameserver": ["223.5.5.5", "114.114.114.114"],
        "nameserver": ["223.5.5.5", "114.114.114.114", "119.29.29.29", "180.76.76.76"],
        "enhanced-mode": "fake-ip",
        "fake-ip-range": "198.18.0.1/16",
        "fake-ip-filter": [
            "*.lan", "*.localdomain", "*.example", "*.invalid", "*.localhost", 
            "*.test", "*.local", "*.home.arpa", "router.asus.com", 
            "localhost.sec.qq.com", "localhost.ptlogin2.qq.com", "+.msftconnecttest.com"
        ]
    },
    "tun": {
        "enable": True,
        "stack": "system",
        "auto-route": True,
        "auto-detect-interface": True,
        "dns-hijack": [
            "114.114.114.114", "180.76.76.76", "119.29.29.29", "223.5.5.5",
            "8.8.8.8", "8.8.4.4", "1.1.1.1", "1.0.0.1"
        ]
    }
}

# 创建支持重试的请求会话
def create_retry_session(retries=5, backoff_factor=0.3, status_forcelist=(500, 502, 503, 504)):
    session = requests.Session()
    retry = Retry(
        total=retries,
        read=retries,
        connect=retries,
        backoff_factor=backoff_factor,
        status_forcelist=status_forcelist,
    )
    adapter = HTTPAdapter(max_retries=retry)
    session.mount('http://', adapter)
    session.mount('https://', adapter)
    return session

def login(session, url):
    try:
        data = {'username': USERNAME, 'password': PASSWORD}
        response = session.post(f'{url}/login', data=data, headers=HEADERS, verify=False, timeout=5)
        if response.status_code != 200:
            logger.error(f'❌ {url} HTTP错误: 状态码 {response.status_code}')
            return None
        response_json = response.json()
        if response_json.get('success'):
            logger.success(f'✅ {url} 登录成功')
            return session.cookies
    except requests.exceptions.RequestException as e:
        logger.error(f'❌ {url} 请求错误: {e}')
    return None

def get_urls(input_file, output_file):
    input_path = Path(input_file)
    output_path = Path(output_file)
    urls_to_write = []

    logger.info(f'📂 正在读取输入文件: {input_path}')
    
    with open(input_path, 'r') as f:
        lines = f.readlines()

    logger.info(f'🔍 开始测试 {len(lines)} 个URL的连通性...')
    
    with create_retry_session() as session, concurrent.futures.ThreadPoolExecutor(max_workers=50) as executor:
        futures = {executor.submit(login, session, line.strip()): line.strip() for line in lines}
        for future in concurrent.futures.as_completed(futures):
            url = futures[future]
            try:
                result = future.result()
                if result:
                    urls_to_write.append(url + '\n')
            except Exception as e:
                logger.error(f'❌ 处理 {url} 时出错: {e}')

    logger.info(f'✅ 成功连接 {len(urls_to_write)} 个URL，保存到: {output_path}')
    
    with open(output_path, 'w') as ff:
        ff.writelines(urls_to_write)

def get_vmess_api(session, url, country, index):
    cookies = login(session, url)
    parsed_url = urlparse(url)
    ip_address = parsed_url.hostname

    if cookies:
        try:
            response = session.post(
                f'{url}/xui/inbound/list',
                headers=HEADERS,
                verify=False,
                cookies=cookies,
                timeout=(5, 10),
            )
            response.raise_for_status()
            response_json = response.json()
        except requests.exceptions.RequestException as e:
            logger.error(f"❌ {url} 请求失败: {e}")
            return None, index

        obj = response_json.get("obj", [])
        for item in obj:
            if item["protocol"] == "vmess":
                settings = json.loads(item["settings"])
                stream_settings = json.loads(item["streamSettings"])

                vmess_info = {
                    "v": "2",
                    "ps": "new",
                    "add": stream_settings.get('tlsSettings', {}).get('serverName', ip_address),
                    "port": item["port"],
                    "id": settings['clients'][0]['id'],
                    "aid": settings['clients'][0].get('alterId', 0),
                    "net": stream_settings['network'],
                    "type": "none",
                    "host": "",
                    "path": stream_settings.get('wsSettings', {}).get('path', "/"),
                    "tls": stream_settings['security']
                }
                vmess_json = json.dumps(vmess_info)
                vmess_base64 = base64.b64encode(vmess_json.encode()).decode()
                return rename_vps(f"vmess://{vmess_base64}", country, index), index + 1
            else:
                logger.info(f"ℹ️ {url} 当前协议: {item['protocol']}")

    return None, index

def rename_vps(data, country, index):
    area_flags = {
        'Usa': '🇺🇸',
        'Singapore': '🇸🇬',
        'Japan': '🇯🇵',
        'Korea': '🇰🇷',
        'Hong_Kong': '🇭🇰',
        'Taiwan': '🇨🇳'
    }
    area_names = {
        'Usa': '美国',
        'Singapore': '新加坡',
        'Japan': '日本',
        'Korea': '韩国',
        'Hong_Kong': '香港',
        'Taiwan': '台湾'
    }
    flag = area_flags.get(country, '🏳️')
    area_name_cn = area_names.get(country, '未知')
    vmess_data = data.split("://")[1]
    decoded_vmess = base64.b64decode(vmess_data).decode('utf-8')

    logger.debug(f'🔍 解码VMess数据: {decoded_vmess}')

    vmess_dict = json.loads(decoded_vmess)
    vmess_dict["ps"] = f"{flag}{area_name_cn}{index}"

    edited_vmess_json = json.dumps(vmess_dict, ensure_ascii=False)
    logger.debug(f'✏️ 修改后VMess数据: {edited_vmess_json}')

    edited_vmess_base64 = base64.b64encode(edited_vmess_json.encode('utf-8')).decode('utf-8')
    vmess_url = 'vmess://' + edited_vmess_base64
    return vmess_url

def parse_vmess_url(vmess_url):
    """解析VMess URL为Clash格式的代理配置"""
    try:
        # 去掉 vmess:// 前缀并解码
        vmess_data = base64.b64decode(vmess_url.replace('vmess://', '')).decode('utf-8')
        vmess_info = json.loads(vmess_data)
        
        # 转换为Clash格式
        proxy_config = {
            "name": vmess_info.get("ps", "Unknown"),
            "type": "vmess",
            "server": vmess_info.get("add", ""),
            "port": int(vmess_info.get("port", 443)),
            "uuid": vmess_info.get("id", ""),
            "alterId": int(vmess_info.get("aid", 0)),
            "cipher": "auto",
            "network": vmess_info.get("net", "tcp"),
            "tls": vmess_info.get("tls") == "tls",
            "ip-version": "dual"
        }
        
        # 如果是WebSocket，添加相关配置
        if vmess_info.get("net") == "ws":
            proxy_config["ws-path"] = vmess_info.get("path", "/")
            if vmess_info.get("host"):
                proxy_config["ws-headers"] = {"Host": vmess_info.get("host")}
            else:
                proxy_config["ws-headers"] = ""
        
        return proxy_config
    except Exception as e:
        logger.error(f"❌ 解析VMess URL失败: {e}")
        return None

def generate_clash_config(proxies_list, output_file):
    """生成完整的Clash配置文件"""
    logger.info(f"🔧 开始生成Clash配置文件，共 {len(proxies_list)} 个代理节点")
    
    # 复制基础配置
    config = CLASH_CONFIG_TEMPLATE.copy()
    
    # 添加当前时间到配置头部注释
    current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    config_header = f"""# 模版名称：ACL4SSR.ini
# 模版最后更新日期：2025/02/11 01:22:37
# 配置生成日期：{current_time}

"""
    
    # 设置代理列表
    config["proxies"] = proxies_list
    
    # 获取所有代理节点名称
    proxy_names = [proxy["name"] for proxy in proxies_list]
    
    # 生成代理组
    config["proxy-groups"] = [
        {
            "name": "🚀 节点选择",
            "type": "select",
            "proxies": ["♻️ 自动选择", "DIRECT"] + proxy_names
        },
        {
            "name": "♻️ 自动选择",
            "type": "url-test",
            "url": "http://www.gstatic.com/generate_204",
            "interval": 300,
            "tolerance": 5000,
            "proxies": proxy_names
        },
        {
            "name": "🌍 国外媒体",
            "type": "select",
            "proxies": ["🚀 节点选择", "♻️ 自动选择", "🎯 全球直连"] + proxy_names
        },
        {
            "name": "📲 电报信息",
            "type": "select",
            "proxies": ["🚀 节点选择", "🎯 全球直连"] + proxy_names
        },
        {
            "name": "Ⓜ️ 微软服务",
            "type": "select",
            "proxies": ["🎯 全球直连", "🚀 节点选择"] + proxy_names
        },
        {
            "name": "🍎 苹果服务",
            "type": "select",
            "proxies": ["🚀 节点选择", "🎯 全球直连"] + proxy_names
        },
        {
            "name": "🎯 全球直连",
            "type": "select",
            "proxies": ["DIRECT", "🚀 节点选择", "♻️ 自动选择"]
        },
        {
            "name": "🛑 全球拦截",
            "type": "select",
            "proxies": ["REJECT", "DIRECT"]
        },
        {
            "name": "🍃 应用净化",
            "type": "select",
            "proxies": ["REJECT", "DIRECT"]
        },
        {
            "name": "🐟 漏网之鱼",
            "type": "select",
            "proxies": ["🚀 节点选择", "🎯 全球直连", "♻️ 自动选择"] + proxy_names
        }
    ]
    
    # 添加完整的规则集
    config["rules"] = get_clash_rules()
    
    # 写入配置文件
    with open(output_file, 'w', encoding='utf-8') as f:
        f.write(config_header)
        yaml.dump(config, f, allow_unicode=True, default_flow_style=False, sort_keys=False, indent=2)
    
    logger.success(f"✅ Clash配置文件已生成: {output_file}")
    logger.info(f"📊 配置统计:")
    logger.info(f"   - 代理节点: {len(proxies_list)} 个")
    logger.info(f"   - 代理组: {len(config['proxy-groups'])} 个")
    logger.info(f"   - 规则: {len(config['rules'])} 条")

def save_vmess_links(vmess_link, filename):
    with open(filename, 'a+') as file:
        file.write(vmess_link + '\n')
    logger.debug(f'💾 VMess链接已保存到 {filename}')

def run(input_dir, output_dir, input_files, vmess=False, generate_clash=True):
    all_proxies = []  # 存储所有解析的代理配置
    
    logger.info(f"🚀 开始运行代理爬虫程序")
    logger.info(f"📁 输入目录: {input_dir}")
    logger.info(f"📁 输出目录: {output_dir}")
    logger.info(f"📋 处理文件: {input_files}")
    
    for input_file_name in input_files:
        country = Path(input_file_name).stem
        input_file = Path(input_dir) / input_file_name
        date_str = time.strftime("%Y%m%d")
        output_file = Path(output_dir) / f'{date_str}_{input_file.stem}_defaultpasswd.txt'
        
        logger.info(f"🌐 处理国家/地区: {country}")
        get_urls(input_file, output_file)

        if vmess:
            vmess_output_file = Path(output_dir) / f'{date_str}_vmess_{input_file.stem}.txt'
            index = 1
            
            logger.info(f"🔄 开始获取VMess配置...")
            
            with open(output_file, 'r') as file:
                urls = file.read().splitlines()
            
            vmess_links = []
            with create_retry_session() as session:
                for url in urls:
                    result, index = get_vmess_api(session, url, country, index)
                    if result:
                        save_vmess_links(result, vmess_output_file)
                        vmess_links.append(result)
            
            logger.info(f"✅ 成功获取 {len(vmess_links)} 个VMess链接")
            
            # 如果需要生成Clash配置，解析VMess链接
            if generate_clash and vmess_links:
                logger.info(f"🔧 解析VMess链接为Clash格式...")
                for vmess_link in vmess_links:
                    proxy_config = parse_vmess_url(vmess_link)
                    if proxy_config:
                        all_proxies.append(proxy_config)
                logger.info(f"✅ 成功解析 {len(all_proxies)} 个代理配置")
    
    # 生成Clash配置文件
    if generate_clash and all_proxies:
        date_str = time.strftime("%Y%m%d")
        clash_output_file = Path(output_dir).parent / f'{date_str}.yaml'
        generate_clash_config(all_proxies, clash_output_file)
    
    logger.success(f"🎉 程序执行完成！")
    
    if all_proxies:
        logger.info(f"📄 生成的文件:")
        logger.info(f"   - VMess链接文件: output/{date_str}_vmess_*.txt")
        logger.info(f"   - Clash配置文件: {date_str}.yaml")
    else:
        logger.warning(f"⚠️ 未获取到有效的代理配置")

if __name__ == '__main__':
    current_directory = Path(__file__).parent
    input_dir = current_directory / 'input'
    output_dir = current_directory / 'output'
    input_files = ['Usa.txt']
    
    # 确保输出目录存在
    output_dir.mkdir(exist_ok=True)
    
    logger.info("=" * 60)
    logger.info("🎯 代理爬虫程序启动")
    logger.info("=" * 60)
    
    run(input_dir, output_dir, input_files, vmess=True, generate_clash=True)
